package com.ruoyi.system.openai.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.constant.RedisKeyMaps;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.OpenAIUrlStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ChatResponse;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.dto.ChatRequest;
import com.ruoyi.system.openai.RequestOpenAi;
import com.ruoyi.system.openai.listener.OpenAISSEEventSourceListener;
import com.ruoyi.system.service.IOpenaiKeysService;
import com.ruoyi.system.service.IOpenaiUrlService;
import com.unfbx.chatgpt.OpenAiClient;
import com.unfbx.chatgpt.OpenAiStreamClient;
import com.unfbx.chatgpt.entity.chat.ChatCompletion;
import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse;
import com.unfbx.chatgpt.entity.chat.Message;
import com.unfbx.chatgpt.exception.BaseException;
import com.unfbx.chatgpt.function.KeyRandomStrategy;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class RequestOpenAiImpl implements RequestOpenAi {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    IOpenaiUrlService openaiUrlService;
    @Autowired
    IOpenaiKeysService openaiKeysService;
    OpenAiStreamClient openAiStreamClient;
    private final OkHttpClient okHttpClient;
    @Autowired
    public RequestOpenAiImpl(OkHttpClient okHttpClient) {
        this.okHttpClient = okHttpClient;
    }

    @Override
    public ChatResponse sseChat(String uid, ChatRequest chatRequest) {
        if (StrUtil.isBlank(chatRequest.getMsg())) {
            throw new BaseException("参数异常，msg不能为空~");
        }
        //判断这个用户是否有对话，对话如果超过十条就删除前面的几条
        List<Message> cacheList = redisCache.getCacheList("msg" + uid);
        List<Message> messages = new ArrayList<>();
        if(cacheList!=null){
            if (cacheList.size() >= 10) {
                messages = messages.subList(1, 10);
            }
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();
            messages.add(currentMessage);
        }else {
            Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build();
            messages.add(currentMessage);
        }

        SseEmitter sseEmitter = redisCache.getCacheObject(uid);

        if (sseEmitter == null) {
            throw new BaseException("聊天消息推送失败uid:[{}],没有创建连接，请重试。~");
        }
        OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(sseEmitter);
        ChatCompletion completion = ChatCompletion
                .builder()
                .messages(messages)
                .temperature(0.9)
                .maxTokens(4096)
                .model(ChatCompletion.Model.GPT_3_5_TURBO.getName())
                .build();

        openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
        redisCache.setCacheObject("msg" + uid, JSONUtil.toJsonStr(messages),5, TimeUnit.MINUTES);
        ChatResponse response = new ChatResponse();
        response.setQuestionTokens(completion.tokens());
        return response;
    }

    @Override
    public List<Message> send(ChatRequest chatRequest) {
        if(StringUtils.isNull(chatRequest)){
            throw new ServiceException("请求参数异常！chatRequest请求参数为空！");
        }
        if(StringUtils.isNull(chatRequest.getCode())||StringUtils.isEmpty(chatRequest.getMsg())){
            throw new ServiceException("请求参数异常！请求参数code或者msg不能为空！");
        }
        StringBuilder sb = new StringBuilder();

        if(chatRequest.getCode().equals(1)){
            sb.append("请为我生成10个关于'")
                    .append(chatRequest.getMsg())
                    .append("'的SEO友好文章标题。请尽量使用疑问句式，并避免使用中文冒号。请在标题中使用相似词、近义词和其他相关词汇来拓展这个关键词。关键词可以是较长或较短的版本，但不能完全相同。请帮助我优化这些标题以提高搜索排名。");
        }else if(chatRequest.getCode().equals(2)){
            sb.append("请根据以下标题撰写一篇文章：'")
                    .append(chatRequest.getMsg())
                    .append("'。在文章内容中，请紧密围绕标题主题，并详细解释相关政策或活动。文章中不需要重复去描述标题，但要确保内容与标题紧密相关。文章的字数尽量超越800字，以便为读者提供充足的信息。同时，确保文章遵循以下SEO优化手段和技巧：\n" +
                    "\n" +
                    "关键词策略：在文章中自然地使用主题相关的关键词和长尾关键词，避免关键词堆砌。\n" +
                    "标题和副标题：使用有趣且包含关键词的标题和副标题，以吸引读者注意力并提高点击率。\n" +
                    "文章结构：确保文章具有清晰的逻辑结构，使用段落和列表使内容易于阅读。\n" +
                    "内容质量：撰写独特、有价值且易于理解的内容，以吸引读者并提高搜索排名。\n" +
                    "文章应为读者提供实用的建议和操作指南。");
        }else {
            throw new ServiceException("请求参数异常！请求参数code不正确！");
        }
        System.out.println(sb.toString());
        List<Message> messages = new ArrayList<>();
        //聊天模型：gpt-3.5
        Message currentMessage = Message.builder().content(sb.toString()).role(Message.Role.USER).build();
        messages.add(currentMessage);
        List<Message> result = sendGPT(messages);

        System.out.println(result.toString());
        return messages;
        /*List<Message> messages = new ArrayList<>();
        Message.builder().content(sb.toString()).role(Message.Role.USER).build();

        ChatCompletion completion = ChatCompletion
                .builder()
                .messages(messages)
                .temperature(0.9)
                .maxTokens(4096)
                .model(ChatCompletion.Model.GPT_3_5_TURBO.getName())
                .build();

        ConsoleEventSourceListener eventSourceListener = new ConsoleEventSourceListener();
        openAiStreamClient.streamChatCompletion(completion, eventSourceListener);*/
    }

    public List<Message> sendGPT(List<Message> msgList){

        List<String> apiKey = getApiKey();
        String apiHost = getApiHost();
        //构建客户端
        OpenAiClient openAiClient = OpenAiClient.builder()
                .apiHost(apiHost)
                .apiKey(apiKey)
                .keyStrategy(new KeyRandomStrategy())
                .okHttpClient(okHttpClient)
                //设置请求地址，请求key
                .build();
        System.out.println("=============请求GPT参数===================");
        System.out.println(msgList);
        ChatCompletion chatCompletion = ChatCompletion
                .builder()
                .messages(msgList)
                .temperature(0.9)
                .model(ChatCompletion.Model.GPT_3_5_TURBO.getName())
                .build();

        ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
        /*//需要捕获两个异常，一个是账号key用完了，一个是请求频繁
        try{
        }catch (HttpException e){
            // 检查异常是否为HTTP 429 Too Many Requests
            if (e.code() == 429) {
                // 创建一个ScheduledExecutorService实例
                ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

                // 创建一个Callable任务
                Callable<ChatCompletionResponse> task = () -> {
                    return openAiClient.chatCompletion(chatCompletion);
                };

                // 在10秒后执行任务并获取Future对象
                Future<ChatCompletionResponse> future = scheduledExecutorService.schedule(task, 30, TimeUnit.SECONDS);

                // 关闭ScheduledExecutorService，但在所有任务完成之前不会关闭
                scheduledExecutorService.shutdown();

                try {
                    // 获取任务执行后的结果
                    chatCompletionResponse = future.get();
                } catch (InterruptedException | ExecutionException ex) {
                    ex.printStackTrace();
                }
            } else {
                // 处理其他类型的HttpException
                e.printStackTrace();
            }

        }*/
        System.out.println("=============请求GPT返回参数==============");
        System.out.println(chatCompletionResponse);
        List<Message> msgLis = new ArrayList<>();
        chatCompletionResponse.getChoices().forEach(e -> {
            msgLis.add(e.getMessage());
        });

        return msgLis;
    }

    public List<String> getApiKey(){

        List<String> openAiKeys = redisCache.getCacheObject(RedisKeyMaps.OPENAI_API_KEYS);

        if(openAiKeys!=null && openAiKeys.size()>0){
            return openAiKeys;
        }
        List<String> apiKey = openaiKeysService.getApiList();
        redisCache.setCacheObject(RedisKeyMaps.OPENAI_API_KEYS,apiKey,1, TimeUnit.DAYS);
        System.out.println("APIKEY未找到，开始查询数据库");
        System.out.println("================apiKey=================");
        apiKey.forEach(System.out::println);
        return apiKey;
    }

    public String getApiHost() {

        String openAiURL = redisCache.getCacheObject(RedisKeyMaps.OPENAI_PROXY_URL);

        if(StringUtils.isNotNull(openAiURL)){
            return openAiURL;
        }
        String url = openaiUrlService.getByValidStatus(OpenAIUrlStatus.OK.getCode());
        redisCache.setCacheObject(RedisKeyMaps.OPENAI_PROXY_URL,url, 30, TimeUnit.DAYS);

        System.out.println("================apiHost=================");
        System.out.println(url);

        return url;
    }
}
